How It Works
When you first render a template, LiveView sends both static and dynamic parts to the client. On subsequent renders, only changed dynamic parts are sent.Initial Render
Consider this template:- Static parts:
<h1>and</h1> - Dynamic parts:
expand_title(@title)
Subsequent Renders
If@title doesn’t change, nothing is sent. The dynamic part is not even executed.
If @title changes, only the new result of expand_title(@title) is sent.
The Rendered Struct
Fromlib/phoenix_live_view/engine.ex:100-126:
:static: List of literal strings (optimized by compiler):dynamic: Function that returns dynamic content when called withtrack_changes?boolean:fingerprint: Unique identifier for the template:root: Whether this is a root template
Fingerprints
Fingerprints identify templates uniquely. Fromlib/phoenix_live_view/engine.ex:1323-1334:
Assign-Level Tracking
Change tracking works at the assign level:@user.name changes but @user.id doesn’t:
@user.nameis re-rendered and sent@user.idis not executed or sent
The __changed__ Map
From lib/phoenix_live_view/engine.ex:392-398:
__changed__ map tracks which assigns have been modified:
Checking for Changes
Fromlib/phoenix_live_view/engine.ex:1395-1401:
Nested Field Tracking
LiveView tracks changes through nested map/struct fields:lib/phoenix_live_view/engine.ex:1412-1428:
Function Components
Function components participate in change tracking:@user.name changes will the component re-render.
Explicit Attributes
Fromlib/phoenix_live_view/engine.ex:750-829:
Common Pitfalls
Variables in Templates
Variables disable change tracking:lib/phoenix_live_view/engine.ex:1292-1321:
Accessing assigns Directly
Never access the assigns variable in templates:
Using Map Functions
Never useMap.put/3 or Map.merge/2 on assigns:
guides/server/assigns-eex.md:243-269):
If you modify the assigns variable with Map.put/3, those assigns inside your HEEx template will not update after the initial render.
Comprehensions
LiveView optimizes comprehensions to track individual items:Without Keys
By default, index-based tracking is used. Inserting at the beginning causes all items to be re-sent.With Keys
Using:key enables ID-based tracking:
Memory Trade-offs
From the guides (guides/server/assigns-eex.md:308-313):
To track changes in comprehensions, LiveView needs to perform additional bookkeeping, which requires extra memory on the server. If memory usage is a concern, you should also consider using Phoenix.LiveView.stream/4, which allows you to manage collections without keeping them in memory.
The Diff Algorithm
Fromlib/phoenix_live_view/diff.ex:134-162:
traverse/6 function walks the rendered tree and builds a minimal diff.
Conditional Rendering
Fromlib/phoenix_live_view/engine.ex:665-687:
nil is returned, signaling “no change” to the diff engine.
Performance Impact
Wire Efficiency
Proper change tracking reduces payload sizes by 90%+ for typical updates:CPU Usage
Change tracking adds minimal CPU overhead:- Template compilation: One-time cost, generates efficient bytecode
- Runtime checks: Simple map lookups in
__changed__ - Diff generation: Only processes changed parts
Memory Usage
The__changed__ map is small (typically less than 10 keys) and cleared after each render.
Best Practices
Debugging Change Tracking
Enable debug logging:Summary
Change tracking is automatic and transparent when you follow best practices:- Use assigns for all dynamic data
- Avoid variables in templates
- Use LiveView functions (
assign/3,update/3) to modify assigns - Be explicit with component attributes